#!/usr/bin/env python
#
# Copyright 2009 Denis Mingulov
#
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

from google.appengine.api import users
from google.appengine.ext import db
from google.appengine.ext.db import polymodel

import math

import persons
import modules


def getCachedSpecabilityPropName(name, relative, priority):
    return "_specabil_%s_rel%dpr%d" % (name, int(relative), priority)


class Fighter(polymodel.PolyModel):
    person = db.ReferenceProperty(persons.Person, required=True, indexed=True)

    # level
    persLevel             = db.IntegerProperty(default=0)

    # characteristics
    persConsitution       = db.IntegerProperty(default=0)
    persWeaponLaser       = db.IntegerProperty(default=0)
    persWeaponPlasma      = db.IntegerProperty(default=0)
    persWeaponRocket      = db.IntegerProperty(default=0)
    persWeaponEnergy      = db.IntegerProperty(default=0)
    persWeaponMiner       = db.IntegerProperty(default=0)
    persWeaponGravity     = db.IntegerProperty(default=0)
    persReaction          = db.IntegerProperty(default=0)
    persEndurance         = db.IntegerProperty(default=0)
    persObservation       = db.IntegerProperty(default=0)
    persIntuition         = db.IntegerProperty(default=0)
    persEngineer          = db.IntegerProperty(default=0)
    persIntellect         = db.IntegerProperty(default=0)
    persDefensiveSys      = db.IntegerProperty(default=0)
    persJammingSys        = db.IntegerProperty(default=0)
    persBiomodule         = db.IntegerProperty(default=0)
    persWorker            = db.IntegerProperty(default=0)
    persSciPhysic         = db.IntegerProperty(default=0)
    persSciPower          = db.IntegerProperty(default=0)
    persSciMath           = db.IntegerProperty(default=0)
                          
    shipOd                = db.IntegerProperty(default=0)
    shipMaxLife           = db.IntegerProperty(default=0)
    shipManeuverability   = db.IntegerProperty(default=0)
    shipAntiAccuracy      = db.IntegerProperty(default=0)
    shipBaseAccuracy      = db.IntegerProperty(default=0)
    shipHitReduce         = db.IntegerProperty(default=0)
                          
    # ship                
    shipTotalCells        = db.IntegerProperty(default=0)
    shipUsedCells         = db.IntegerProperty(default=0)
    shipWeight            = db.IntegerProperty(default=0)
    shipEnergy            = db.IntegerProperty(default=0)
    shipPower             = db.IntegerProperty(default=0)
    shipBattleCompSpeed   = db.IntegerProperty(default=0)
    shipNavCompSpeed      = db.IntegerProperty(default=0)
    shipContainerCapacity = db.IntegerProperty(default=0)
    shipHangarCapacity    = db.IntegerProperty(default=0)
    shipFuelTankCapacity  = db.IntegerProperty(default=0)
    shipHoldCapacity      = db.IntegerProperty(default=0)
    shipPlanetEngineSpeed = db.IntegerProperty(default=0)
    shipHyperEngineSpeed  = db.IntegerProperty(default=0)
    shipShield            = db.IntegerProperty(default=0)
    shipProtection        =  db.IntegerProperty(default=0)
    shipLongRangeRadarSensitivity = db.IntegerProperty(default=0)
    shipNavRadarSensitivity       = db.IntegerProperty(default=0)
    shipBattleRadarSensitivity    = db.IntegerProperty(default=0)
    shipJammerRadarSensitivity    = db.IntegerProperty(default=0)


    def generateSpecialAbilitiesTable(self, modlist = None):
        if modlist is None:
            modlist = persons.getActiveModules(self.person)
        for mod in modlist:
            specabilities = modules.getModuleTypeSpecialAbilities(mod.type)
            if specabilities is not None:
                for specabil in specabilities:
                    prop = getCachedSpecabilityPropName(specabil.name, specabil.relative, specabil.priority)
                    value = 0
                    if hasattr(self, prop):
                         value = getattr(self, prop)
                    value += specabil.value
                    setattr(self, prop, value)


    def calculateLoadedSpecialAbility(self, origvalue, modlist, param, relative, priority):
        prop = getCachedSpecabilityPropName(param, relative, priority)
        if not hasattr(self, prop):
            return origvalue
        value = getattr(self, prop)
        if relative:
            origvalue *= 1 + float(value)/100
        else: 
            origvalue += value

        return origvalue

    def calculateSpecialAbility(self, modlist, param):
        if modlist is None:
            modlist = persons.getActiveModules(self.person)
        if hasattr(self, param):
            val = getattr(self, param)
            origvalue = 0.0
            if val is not None:
                origvalue = float(getattr(self, param))
            origvalue = self.calculateLoadedSpecialAbility(modlist=modlist, origvalue=origvalue, param=param, relative=True, priority=1);
            origvalue = self.calculateLoadedSpecialAbility(modlist=modlist, origvalue=origvalue, param=param, relative=False, priority=1);
            origvalue = self.calculateLoadedSpecialAbility(modlist=modlist, origvalue=origvalue, param=param, relative=False, priority=0);
            origvalue = self.calculateLoadedSpecialAbility(modlist=modlist, origvalue=origvalue, param=param, relative=True, priority=0);
            setattr(self, param, long(math.floor(0.5+origvalue)))

    def calculateSpecialAbilities(self, paramlist):
        modlist = persons.getActiveModules(self.person)
        for param in paramlist:
            self.calculateSpecialAbility(modlist=modlist, param=param)


    def calculatePersonSpecialAbilities(self):
        params = ['persConsitution', 'persWeaponLaser', 'persWeaponPlasma', 'persWeaponRocket', 'persWeaponEnergy',
                  'persWeaponMiner', 'persWeaponGravity', 'persReaction', 'persEndurance', 'persObservation',
                  'persIntuition', 'persEngineer', 'persIntellect', 'persDefensiveSys', 'persJammingSys',
                  'persBiomodule', 'persWorker', 'persSciPhysic', 'persSciPower', 'persSciMath'
        ]
        self.calculateSpecialAbilities(paramlist = params)

    def calculateShipSpecialAbilities1(self):
        params = [
            'shipBattleCompSpeed',
            'shipNavCompSpeed',
            'shipHyperEngineSpeed',
            'shipPlanetEngineSpeed',
            'shipBattleRadarSensitivity',
            'shipJammerRadarSensitivity',
            'shipLongRangeRadarSensitivity',
            'shipNavRadarSensitivity',
            'shipPower',
            'shipShield',
            'shipManeuverability',
            'shipProtection',
            'shipOd',
        ]
        self.calculateSpecialAbilities(paramlist = params)

    def calculateShipSpecialAbilities2(self):
        params = [
            'shipAntiAccuracy',
            'shipMaxLife',
#            'shipBaseAccuracy', # do not use
        ]
        self.calculateSpecialAbilities(paramlist = params)

    def calculateDifferentShipValues1(self):
        self.calculateShipShield()
        self.calculateShipRadars()
        self.calculateShipComputers()

        self.calculateShipManeuverability()
        self.calculateShipOd()
        self.calculateShipProtection()

    def calculateDifferentShipValues2(self):
        # must be after calculateShipManeuverability
        self.calculateShipMaxLife()
        self.calculateShipAntiAccuracy()
        self.calculateShipBaseAccuracy()

    def calculateShipShield(self):
        self.shipShield = int(math.floor(0.5 + self.shipShield * (1 + 0.03 * self.persDefensiveSys)))

    def calculateShipRadars(self):
        self.shipNavRadarSensitivity    = int(math.floor(0.5 + self.shipNavRadarSensitivity    * (1 + 0.03 * self.persJammingSys)))
        self.shipBattleRadarSensitivity = int(math.floor(0.5 + self.shipBattleRadarSensitivity * (1 + 0.03 * self.persJammingSys)))
        self.shipJammerRadarSensitivity = int(math.floor(0.5 + self.shipJammerRadarSensitivity * (1 + 0.03 * self.persJammingSys)))

    def calculateShipComputers(self):
        self.shipBattleCompSpeed = int(math.floor(0.5 + self.shipBattleCompSpeed * (1 + 0.1 * self.persIntellect)))
        self.shipNavCompSpeed    = int(math.floor(0.5 + self.shipNavCompSpeed    * (1 + 0.1 * self.persIntellect)))

    def calculateShipManeuverability(self):
        if self.shipWeight:
            self.shipManeuverability = 2 * self.shipPlanetEngineSpeed / self.shipWeight;
        else:
            self.shipManeuverability = 0

    def calculateShipOd(self):
        # 10 is magic number
        self.shipOd = 10 + self.persReaction

    def calculateShipMaxLife(self):
        value = 0.0
        value += 20 + (1 + self.persLevel) * self.persLevel/2 - 1;
        value *= 1 + 0.05 * self.persConsitution
        self.shipMaxLife = int(math.floor(0.5+value))

    def calculateShipProtection(self):
        pass

    def calculateShipMaxLife2(self):
        self.shipMaxLife += self.shipProtection

    def calculateShipAntiAccuracy(self):
        self.shipAntiAccuracy = self.persIntuition*2 + self.shipJammerRadarSensitivity/2 + 2 * self.shipManeuverability

    def calculateShipBaseAccuracy(self):
        self.shipBaseAccuracy = self.persObservation*2 + self.shipBattleRadarSensitivity/2

    def calculateWeaponDistance(self, distance):
        modlist = persons.getActiveModules(self.person)
        param = 'weaponMaxDist'
        origvalue = float(distance)
        origvalue = self.calculateLoadedSpecialAbility(modlist=modlist, origvalue=origvalue, param=param, relative=False, priority=0);
        origvalue = self.calculateLoadedSpecialAbility(modlist=modlist, origvalue=origvalue, param=param, relative=True, priority=0);
        return long(math.floor(0.5+origvalue))

    def calculateWeaponAccuracy(self, accuracy):
        modlist = persons.getActiveModules(self.person)
        param = 'shipBaseAccuracy'
        origvalue = float(accuracy + self.shipBaseAccuracy)
        origvalue = self.calculateLoadedSpecialAbility(modlist=modlist, origvalue=origvalue, param=param, relative=False, priority=0);
        origvalue = self.calculateLoadedSpecialAbility(modlist=modlist, origvalue=origvalue, param=param, relative=True, priority=0);
        return long(math.floor(0.5+origvalue))

    def calculateWeaponMinShot(self, minshot):
        modlist = persons.getActiveModules(self.person)
        param = 'weaponMinShot'
        origvalue = float(minshot)
        origvalue = self.calculateLoadedSpecialAbility(modlist=modlist, origvalue=origvalue, param=param, relative=False, priority=0);
        origvalue = self.calculateLoadedSpecialAbility(modlist=modlist, origvalue=origvalue, param=param, relative=True, priority=0);
        origvalue *= self.shipBattleCompSpeed/100 + 1
        return long(math.floor(0.5+origvalue))

    def calculateWeaponMaxShot(self, weapon):
        modlist = persons.getActiveModules(self.person)
        param = 'weaponMaxShot'
        lev = 0;
        if isinstance(weapon, modules.LaserWeapon):
            lev = self.persWeaponLaser 
        if isinstance(weapon, modules.PlasmaWeapon):
            lev = self.persWeaponPlasma
        if isinstance(weapon, modules.RocketWeapon):
            lev = self.persWeaponRocket
        if isinstance(weapon, modules.EnergyWeapon):
            lev = self.persWeaponEnergy
        if isinstance(weapon, modules.MineWeapon):
            lev = self.persWeaponMiner 
        origvalue = float(weapon.maxshot) * (1+0.05*lev)
        origvalue = self.calculateLoadedSpecialAbility(modlist=modlist, origvalue=origvalue, param=param, relative=False, priority=0);
        origvalue = self.calculateLoadedSpecialAbility(modlist=modlist, origvalue=origvalue, param=param, relative=True, priority=0);
        return long(math.floor(0.5+origvalue))
